package cn.ilikes.tools.nosql.jpa.persistences.contexts.operations;

import java.util.LinkedList;
import java.util.Queue;

import org.slf4j.Logger;

import cn.ilikes.tools.logs.Loggerfactory;
import cn.ilikes.tools.nosql.jpa.clients.Client;
import cn.ilikes.tools.nosql.jpa.clients.JPASQLClientsException;
import cn.ilikes.tools.nosql.jpa.graphs.INodeBuilderListener;
import cn.ilikes.tools.nosql.jpa.graphs.INodeGraphBuilder;
import cn.ilikes.tools.nosql.jpa.graphs.Node;
import cn.ilikes.tools.nosql.jpa.graphs.NodeGraphBuilder;
import cn.ilikes.tools.nosql.jpa.graphs.NodeLifeCycle;
import cn.ilikes.tools.nosql.jpa.graphs.INodeGraphBuilder.BuildType;
import cn.ilikes.tools.nosql.jpa.graphs.Node.State;
import cn.ilikes.tools.nosql.jpa.graphs.NodeLifeCycle.LifeCycle;
import cn.ilikes.tools.nosql.jpa.metadatas.EntityMetaData;
import cn.ilikes.tools.nosql.jpa.persistences.contexts.AbstractContext;
import cn.ilikes.tools.nosql.jpa.persistences.contexts.Operation;
import cn.ilikes.tools.nosql.jpa.persistences.contexts.OperationRunTimeException;
import cn.ilikes.tools.nosql.jpa.unitls.PropertyAccessorHelper;

public class PersistOperation extends ContextOperation implements Operation<Boolean> {

	private static Logger logger = Loggerfactory.instance(PersistOperation.class);

	private Queue<Node> stack = new LinkedList<Node>();

	private Queue<Node> saveStack = new LinkedList<Node>();

	private Object entity;

	private INodeGraphBuilder nodeGraphBuilder;

	public PersistOperation(Object entity, AbstractContext context) {
		super(OperationType.insert, context);
		this.entity = entity;
		INodeGraphBuilder nodeGraphBuilder = new NodeGraphBuilder(context.getPersistenUnitEnv(), new INodeBuilderListener() {
			@Override
			public void buildNode(Node node) {
				stack.add(node);
			}
		});
		setNodeGraphBuilder(nodeGraphBuilder);
	}

	private void setNodeGraphBuilder(INodeGraphBuilder nodeGraphBuilder) {
		this.nodeGraphBuilder = nodeGraphBuilder;
	}

	@Override
	public Boolean exec() {
		if (Loggerfactory.isdebug(logger))
			Loggerfactory.debug(logger, "persist peration exec");
		AbstractContext abstractContext = getContext();
		Client client = abstractContext.getClient();
		try {
			while (!stack.isEmpty()) {
				Node node = stack.poll();
				client.save(node);
				Node.switchNodeLifeCycle(node, LifeCycle.managed);
				Object key = PropertyAccessorHelper.getId(node);
				abstractContext.putCache(key, node);
				node.setState(State.Persist);
				saveStack.add(node);
			}
			return Boolean.TRUE;
		} catch (JPASQLClientsException e) {
			throw new OperationRunTimeException(e);
		}
	}

	@Override
	public boolean init() {
		if (Loggerfactory.isdebug(logger))
			Loggerfactory.debug(logger, "PersistOperation   init ");
		AbstractContext context = getContext();
		EntityMetaData entityMetaData = context.getPersistenUnitEnv().getMetaDataManagerFactory().selectIEntityMetaData(entity.getClass());
		Node node = Node.createtNode(entityMetaData, entity, NodeLifeCycle.LifeCycle.newses);
		nodeGraphBuilder.buildNodeStack(node, BuildType.save);
		return true;
	}

	@Override
	public boolean rollback() {
		if (Loggerfactory.isdebug(logger))
			Loggerfactory.debug(logger, "Persist  Operation rollback  ");
		AbstractContext abstractContext = getContext();
		Client client = abstractContext.getClient();

		while (!saveStack.isEmpty()) {
			Node node = saveStack.poll();
			try {
				if (node.getState().equals(State.Persist))
					client.delete(node);
				//				if (Loggerfactory.isdebug(logger)) Loggerfactory.debug(logger, "Persist    rollback  "+node.getEntity().toString());
			} catch (JPASQLClientsException e) {
				Loggerfactory.warn(logger, "Node  " + node.getEntity() + "   roll back no success ! ");
			}
			Node.switchNodeLifeCycle(node, LifeCycle.managed);
			Object key = PropertyAccessorHelper.getId(node);
			abstractContext.putCache(key, node);
		}
		return Boolean.TRUE;
	}

}
